import java.util.ArrayList;
import java.util.Random;

public class Tester {

	private Sorter[] sorter;
	private ArrayList<int[]> arraysToBeSorted;
	private int[] interval;
	private int reps;
	private int last;
	private int first;
	private int steps;

	public Tester(int first, int last, int reps, int steps, Sorter[] sorter) {
		this.reps = reps;
		this.steps = steps;
		this.last = last;
		this.first = first;
		this.sorter = sorter;
	}

	public double[][] startTesting() {
		double[][] sortingTimes;
		sortingTimes = new double[sorter.length][steps];
		makeStepArray();
		makeStepIntervalArray();
		for (int j = 0; j < sorter.length; j++) {

			sortingTimes[j] = test(sorter[j]);
		}
		return sortingTimes;
	}

	public int[] getStepArray() {
		return interval;
	}

	private void makeStepIntervalArray() {
		arraysToBeSorted = new ArrayList<int[]>();
		for (int i = 1; i <= steps; i++) {
			arraysToBeSorted.add(populateArray(interval[0], interval[i]));
		}
	}

	private void makeStepArray() {
		int stepSize = (int) Math.ceil((last - first) / steps);
		interval = new int[steps + 1];
		interval[0] = first;
		// make interval boundary array
		for (int i = 1; i <= steps; i++) {
			interval[i] = interval[i - 1] + stepSize;
		}
	}

	private double[] test(Sorter sort) {
		double starttime;
		double[][] finishTimes = new double[reps][steps + 1];
		int[] arrayCopy;

		for (int i = 0; i < reps; i++) {
			for (int j = 0; j < steps; j++) {
				arrayCopy = new int[arraysToBeSorted.get(j).length];
				System.arraycopy(arraysToBeSorted.get(j), 0, arrayCopy, 0,
						arraysToBeSorted.get(j).length);
				starttime = System.nanoTime();

				sort.sort(arrayCopy, last);
				finishTimes[i][j] = System.nanoTime();
				finishTimes[i][j] = finishTimes[i][j] - starttime;

			}
		}
		return calculateAverageRunTime(finishTimes);
	}

	private double[] calculateAverageRunTime(double[][] finishTimes) {
		double[] runTimes = new double[steps + 1];
		for (int i = 0; i < steps; i++) {
			for (int j = 0; j < reps; j++) {
				runTimes[i] = runTimes[i] + finishTimes[j][i];
			}

			// Creating average and converting to microseconds
			runTimes[i] = runTimes[i] / reps / 1000;

		}
		return runTimes;
	}

	private int[] populateArray(int first, int last) {
		int length = Math.abs(last) - Math.abs(first);
		int[] arr = new int[length + 1];
		for (int i = 0; i <= length; i++) {
			arr[i] = first;
			first++;
		}
		return randomizeArray(arr);
	}

	private int[] randomizeArray(int[] array) {
		Random rnd = new Random();
		for (int i = 0; i < array.length; i++) {
			int tmp = array[i];
			int index = 0 + rnd.nextInt(Math.abs(array.length));
			array[i] = array[index];
			array[index] = tmp;
		}
		return array;
	}

}